#include "lib.h"

#include "lirgame.h"
#include "lirconn.h"
#include "shue.h"
#include "lirhand.h"
#include "cardmgr.h"
#include "lirranks.h"
#include "lirsaved.h"

#define LIR_NUMDECKS	1		// num decks in shue

enum Outcomes
{
	O_PLAYER_LOST,
	O_PLAYER_WON
};

static CardMgr stdMgr;

static int32 sPayTable[MAX_PAY_FREQUENCIES][HR_ROYALFLUSH + 1] =
{
    {	0, 1, 2, 3, 5, 8, 11, 50, 200, 1000   },  // NORMAL pay (frequency) table

    { 	0, 1, 2, 3, 5, 10, 15, 25, 100, 500   }   // ALTERNATE pay (frequency) table

};

static int32 sProgressivePayTable[MAX_PROGRESSIVE_PAY_FREQUENCIES][HR_ROYALFLUSH + 1] =
{
    {   0, 0, 0, 0, 0, 5000, 7500, 10000, 10, 100   },   // LOW progressive pay (frequency) table

    {   0, 0, 0, 0, 0, 5000, 10000, 50000, 10, 100   },   // MEDIUM progressive pay (frequency) table

    {   0, 0, 0, 0, 0, 7500, 15000, 50000, 10, 100   },   // HIGH progressive pay (frequency) table

    {   0, 0, 0, 0, 0, 7500, 10000, 50000, 10, 100   }   // ALT HIGH progressive pay (frequency) table
};



// Rudy 12/206/042: Added this block to allow QA to test
#if (_DEBUG && MAGIC_DECK)

// Deck offsets (feel the thread-unsafeness)
int32 iPlayer = 0;


// Loaded player deck
static const uint8 puPlayerCards[] = {
/*	0x4e, 0x4d, 0x4c, 0x4b,	0x4a,	// royal flush
	0x35, 0x34, 0x33, 0x36, 0x37,	// straight flush*/
	0x2a, 0x1a, 0x4a, 0x3a, 0x23,	// 4 of a kind
	0x2b, 0x1b, 0x4b, 0x4a, 0x1a,	// full house
	0x3a, 0x3b, 0x3e, 0x32, 0x33,	// flush
/*
	0x3b, 0x2c, 0x1d, 0x29, 0x4a,	// Straight
	0x3a, 0x4a, 0x1a, 0x4b, 0x4c,	// 3 of a Kind
	0x3b, 0x2b, 0x39, 0x49, 0x28,	// Two Pair
	0x1e, 0x4e, 0x3d, 0x2b, 0x39,	// Pair of 10 or better */

};

// Get the next card from the player deck
uint8 getPlayerCard()
{
	uint8 uCard = puPlayerCards[iPlayer];
	if (++iPlayer >= sizeof(puPlayerCards)/sizeof(uint8))
		iPlayer = 0;
	return uCard;
}
#endif


LIRGame::LIRGame(LIRConnection* conn, int32 bet, int32 progressiveBet)
{
	this->conn = conn;
	this->bet = bet;
    this->progressiveBet = progressiveBet;
    this->payFreq = conn->getPayTable();
    if (this->payFreq >= MAX_PAY_FREQUENCIES)
        this->payFreq = PAYFREQ_NORMAL;
    this->progPayFreq = conn->getProgressivePayTable();
    if (this->progPayFreq >= MAX_PROGRESSIVE_PAY_FREQUENCIES)
        this->progPayFreq = PROGPAYFREQ_MEDIUM;

    bTurnOneDone = false;
	dealer = new LIRHand(&stdMgr);
	player = new LIRHand(&stdMgr);
    raised1 = false;
    raised2 = false;
    bTurnOneDone = false;
	bGameOK = true;
    m_bMalfunction = false;
}

LIRGame::LIRGame(LIRConnection* conn, LIRSavedGame* saved)
{
	this->conn = conn;

    this->progPayFreq = conn->getProgressivePayTable();
    if (this->progPayFreq >= MAX_PROGRESSIVE_PAY_FREQUENCIES)
        this->progPayFreq = PROGPAYFREQ_MEDIUM;
	// everything else is restored in restore()
	//
	bet = 0;
	dealer = player = (LIRHand*)0;

	bGameOK = restore(saved);
}

LIRGame::~LIRGame()
{
	delete dealer;
	delete player;
}

bool8
LIRGame::deal(int32 balance)
{
	Shue* shue;
	bool8 result = false;

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = MACHID;

    #ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
    #endif


	shue = new Shue(&stdMgr, LIR_NUMDECKS, sgi);

	if (shue && dealer && player)
	{
		shue->shuffle();

        // kharmon.  10-16-06.  #8226.  Save the deck seed, not the cards.
        ShueSave save;
        shue->save(&save);

#if (_DEBUG && MAGIC_DECK)
		player->add(getPlayerCard());
		player->add(getPlayerCard());
		player->add(getPlayerCard());

		dealer->add(getPlayerCard());
		dealer->add(getPlayerCard());
#else
		player->add(shue->getCard());
		player->add(shue->getCard());
		player->add(shue->getCard());

		dealer->add(shue->getCard());
		dealer->add(shue->getCard());
#endif

		delete shue;

        if (conn->trackDeal(save.seed, player->getCount(), player->getCards())) // kharmon.  11-07-06.  #8226.  Save the player cards.
		{
			conn->msgStart();
			conn->msgAdd((uint8)'D');
			conn->msgAdd((uint8)(player->getCount() & 0xff));
			conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
			conn->msgAdd(balance);
			conn->msgEnd();

			result = true;
		}
	}

	return result;
}

bool8
LIRGame::raise1(int32 balance)
{
    raised1 = true;
	return turnOneDone(balance);
}

bool8
LIRGame::raise2(int32 balance)
{
    raised2 = true;
	return gameDone(balance);
}

bool8
LIRGame::continue1(int32 balance)
{
    raised1 = false;
	return turnOneDone(balance);
}

bool8
LIRGame::continue2(int32 balance)
{
    raised2 = false;
	return gameDone(balance);
}

bool8
LIRGame::gameDone(int32 balance)
{
	int32 payouts[3], progressivePayout, balAdjust, playerRank;
	int32 multiBankPayout = 0;
	int32 outcome;

	// luke 7/24/02: This now returns an error when a progressive win fails
	// luke 7/26/02: This now returns the player's hand rank
	if (!calcPayouts(payouts, progressivePayout, multiBankPayout, outcome, balAdjust, playerRank))
		return false;

	conn->trackRemoveSavedGame();

    // kharmon.  10-16-06.  #8226.  Now that the game is over, we can safely write the cards to the database:
	if (conn->trackGameDone(raised2, payouts, progressivePayout, multiBankPayout, 
							outcome, balAdjust, hasMalfunction(), &balance, dealer, player))
	{
		conn->msgStart();
		conn->msgAdd((uint8)'F');
		conn->msgReleaseBuf(dealer->outlast(conn->msgGetBuf()));
		conn->msgAdd(payouts[0]);
		conn->msgAdd(payouts[1]);
		conn->msgAdd(payouts[2]);
		conn->msgAdd(progressivePayout);
		conn->msgAdd((uint8)(playerRank & 0xFF));
		conn->msgAdd((uint8)(outcome & 0xff));
		conn->msgAdd(balance);
        conn->msgAdd((uint8)conn->isProgressiveOn());
        conn->msgAdd((uint8)conn->getPayTable());
        conn->msgAdd((uint8)conn->getProgressivePayTable());
		conn->msgEnd();

        // kharmon.  01-05-06.  #4250.  If the player hit a big progressive jackpot, 
        // this error will shut them down until we can verify things.
        if(hasMalfunction())
        {
            conn->msgStart();
            conn->msgAdd((uint8) 'E');
            conn->msgAdd((uint8) 13);
            conn->msgEnd();
        }

		conn->gameDone();	// delete "this"

		return true;
	}

	return false;
}

bool8
LIRGame::calcPayouts(int32 payouts[3], int32& progressivePayout, int32& multiBankPayout, 
					 int32& outcome, int32& balAdjust, int32& playerRank)
{
	int32 winnings=0, i=0;

    //dealer's hand now gets joined with player's hand to be judged as a regular poker hand

	LIRHand* combinedHand = new LIRHand(&stdMgr);
	for (i=0; i<MAXDEALERCARDS; i++)
	{
	    combinedHand->add(dealer->getCardAt(i));
	}
	for (i=0; i<MAXPLAYERCARDS; i++)
	{
	    combinedHand->add(player->getCardAt(i));
	}

	// luke 7/25/02: Rather than deleting player and replacing it with 
	// combinedHand, use combinedHand directly below (player needs to be 
	// preserved in case the progressive win (for instance) fails

	if (!combinedHand->rankHand())
	{
		delete combinedHand;
		return false;
	}

    playerRank = combinedHand->getRank();

    if ( combinedHand->getRank()> HR_NOTHING)
		outcome = O_PLAYER_WON;
	else
		outcome = O_PLAYER_LOST;

    if (progressiveBet > 0 && sProgressivePayTable[progPayFreq][playerRank] > 0)
	{
		bool8 success;
		int32 potwinner;

		potwinner = sProgressivePayTable[progPayFreq][playerRank];

		if (potwinner != 10 && potwinner != 100)
		{
			potwinner *= conn->getProgressiveFixedAmountMultiplier();
		}

        // kharmon.  06-11-07.  #12205.  If the potwinner is <= 100, then it's a percentage, not
        // an absolute amount.
        bool bPercentage = false;
        if(potwinner<=100)
        {
            bPercentage=true;
        }

		// luke 7/24/02: This now returns an error when a progressive win fails
        success = conn->setProgressiveWinner(potwinner, &progressivePayout, &multiBankPayout, bPercentage);

        multiBankPayout *= 100; // kharmon.  11-15-07.  #20294.  Need to do this for all wins, because we pass them into the db as millimoney, not money.

		if (potwinner == 10 || potwinner == 100)
		{
			conn->SendWinMail(potwinner, progressivePayout);
		}

		if (!success)
		{
			delete combinedHand;
			return false;
		}
	}
    else
        progressivePayout = 0;

	payouts[0] = 0;
    payouts[1] = 0;
    payouts[2] = 0;
	switch (outcome)
	{
		case O_PLAYER_LOST:
			payouts[0] = -bet;
            if (raised1)
			    payouts[1] = -bet;
            if (raised2)
			    payouts[2] = -bet;
			winnings = 0;
			break;

		case O_PLAYER_WON:
			payouts[0] = bet * sPayTable[payFreq][combinedHand->getRank()];
            if (raised1)
    			payouts[1] = payouts[0];
            if (raised2)
       			payouts[2] = payouts[0];

            winnings = bet + payouts[0] +
                      (raised1 ? payouts[1] + bet : 0) +
                      (raised2 ? payouts[2] + bet : 0);
			break;
	}

	// [AO 2007-10-18] Always award jackpot
	balAdjust = winnings + progressivePayout;

	delete combinedHand;
	return true;
}

void
LIRGame::save(LIRSavedGame* saved)
{
	// everything is already stored in the database (no need to store the shue)
	//
}

bool8
LIRGame::restore(LIRSavedGame* saved)
{
    setMalfunction(saved->bMalfunction);    // kharmon.  01-05-06.  #4250.  Restore the malfunction state.

	bet = saved->bet;
	progressiveBet = saved->progressiveBet;
    payFreq = saved->payTable;
    if (this->payFreq >= MAX_PAY_FREQUENCIES)
        this->payFreq = PAYFREQ_NORMAL;

    switch (saved->raisePassTurn1)
    {
        case 'R':
            raised1 = true;
            bTurnOneDone = true;
            break;
        case 'P':
            raised1 = false;
            bTurnOneDone = true;
            break;
        default:
            bTurnOneDone = false;
    }

	dealer = new LIRHand(&stdMgr);
    player = new LIRHand(&stdMgr);


    if(saved->bHasDeckSeed==false)
    {
        // kharmon.  10-16-06.  #8226.  This is an older saved game and doesn't have a deckseed.
        // restore the cards the old way.
	    dealer->restore(saved->dealerCards, saved->numDealerCards);
	    player->restore(saved->playerCards, saved->numPlayerCards);
    }
    else
    {
        // kharmon.  10-16-06.  #8226.  Use the deck seed to regenerate the cards.
	    ShueGameInfo sgi;
	    sgi.gameId = GAMEID;
	    sgi.machId = MACHID;
        
        Shue  *shue = new Shue(&stdMgr, LIR_NUMDECKS, sgi);

        ShueSave save;
        save.seed = saved->nDeckSeed;
        save.next = 0;
        shue->restore(&save);

		player->add(shue->getCard());
		player->add(shue->getCard());
		player->add(shue->getCard());

		dealer->add(shue->getCard());
		dealer->add(shue->getCard());

        delete shue;
    }

	//
	// At this point the server is *exactly* the same as when the game was
	// saved:  all automatic playing had already completed and the game
	// was idle.
	//

	// Make sure we have good hands
	return dealer->rankHand() && player->rankHand() && !hasMalfunction();   // kharmon.  01-05-06.  #4250.  The game is not "OK" if we have a malfunction.
}

void
LIRGame::send()
{
	// This message is contained within another message
	// (don't call msgStart()/msgEnd()).
    conn->msgAdd(bet);
    conn->msgAdd(progressiveBet);

    if (bTurnOneDone)
    {
    	conn->msgAdd((uint8)((uint8)1 & 0xff));
	    conn->msgReleaseBuf(dealer->outfirst(conn->msgGetBuf()));
        conn->msgAdd((uint8)((uint8)raised1 & 0xff));     //send raised flag after dealers 1 card b/c this is turn 2
    }
    else
    {
    	conn->msgAdd((uint8)((uint8)0 & 0xff));
    }

	conn->msgAdd((uint8)(player->getCount() & 0xff));
	conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
    conn->msgAdd(payFreq);
}

bool8
LIRGame::turnOneDone(int32 balance)
{
    if (conn->trackTurnOneDone(raised1, dealer))        // kharmon.  11-07-06.  #8226.  Save the face up dealer card.
	{
		conn->msgStart();
		conn->msgAdd((uint8)'O');
		conn->msgReleaseBuf(dealer->outfirst(conn->msgGetBuf()));
		conn->msgAdd(balance);
		conn->msgEnd();

		return true;
	}
    bTurnOneDone = true;

	return false;
}
